/*
* File: CFdEventPoll.cpp
* Copyright (c) 2010. Silviu Caragea <silviu@intelliproject.net>
* Created on September 15, 2010, 12:34 AM
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*  1.Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*  2.Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*  3.All advertising materials mentioning features or use of this software
*    must display the following acknowledgement:
*       This product includes software developed by the University of
*       California, Berkeley and its contributors.
*  4.Neither the name of the University nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "CFdEventPoll.h"

#ifdef USE_POLL

namespace litevent
{


int CFdEventPoll::setEvent(int fd_node , int fd, int events)
{
    int pollEvents = 0;
    if (events & FDEVENT_IN) pollEvents |= POLLIN;
    if (events & FDEVENT_OUT)pollEvents |= POLLOUT;

    /* known index */

    if (fd_node != -1)
    {
	if (this->pollfds[fd_node].fd == fd)
        {
            this->pollfds[fd_node].events = pollEvents;
            return fd_node;
	}

        util::CLogger::sharedLogger()->WriteFormated("%s.%d: CFdEventPoll::setEvent fd not match: %d , %d ", __FILE__, __LINE__,this->pollfds[fd_node].fd,fd);
        return -1;
    }

    if (this->unused.used > 0)
    {
    	int k = this->unused.ptr[--this->unused.used];
	this->pollfds[k].fd = fd;
	this->pollfds[k].events = pollEvents;
	return k;
    }
    else
    {
	if (this->size == 0)
        {
            this->size = BUFFER_INT_SIZE;
            this->pollfds = (pollfd*) malloc(sizeof(*this->pollfds) * this->size);
	}
        else if (this->size == this->used)
        {
            this->size += BUFFER_INT_SIZE;
            this->pollfds = (pollfd*) realloc(this->pollfds, sizeof(*this->pollfds) * this->size);
	}

        this->pollfds[this->used].fd = fd;
	this->pollfds[this->used].events = pollEvents;
	return this->used++;
    }

}

int CFdEventPoll::deleteEvent(int fd_node, int fd )
{
    if (fd_node < 0)
        return 0;

    if ((size_t)fd_node >= this->used)
    {
        util::CLogger::sharedLogger()->WriteFormated("%s.%d: CFDEventPoll::deleteEvent out of range ", __FILE__, __LINE__);
        return 0;
    }

    if (this->pollfds[fd_node].fd == fd)
    {
	size_t k = fd_node;
	this->pollfds[k].fd = -1;

        if (this->unused.size == 0)
        {
            this->unused.size = BUFFER_INT_SIZE;
            this->unused.ptr = (int*)malloc(sizeof(*(this->unused.ptr)) * this->unused.size);
        }
        else if (this->unused.size == this->unused.used)
        {
            this->unused.size += BUFFER_INT_SIZE;
            this->unused.ptr = (int*)realloc(this->unused.ptr, sizeof(*(this->unused.ptr)) * this->unused.size);
	}

	this->unused.ptr[this->unused.used++] = k;
    }
    else
    {
        util::CLogger::sharedLogger()->WriteFormated("%s.%d: CFDEventPoll::deleteEvent fd not match: %d , %d ", __FILE__, __LINE__,this->pollfds[fd_node].fd,fd);
        return 0;
    }

    return 1;
}


int CFdEventPoll::poll(int timeoutMs)
{
    //-1 -> wait infinite
    // 0 -> return immediatly
    
    return ::poll(this->pollfds, this->used, timeoutMs);
}

int CFdEventPoll::getEventRevent(size_t node)
{
    if (node >= this->used)
    {
        util::CLogger::sharedLogger()->WriteFormated("%s.%d: CFdEventPoll::getEventRevent node >used fd( %d > %d )", __FILE__, __LINE__,(int) node,(int)this->used);
	return 0;
    }

    if (this->pollfds[node].revents & POLLNVAL)
    {	
        util::CLogger::sharedLogger()->WriteFormated("%s.%d: CFdEventPoll::getEventRevent invalid fd", __FILE__, __LINE__);
	return 0;
    }

    int revents = 0;
    int pollRevents = this->pollfds[node].revents;
        
    if (pollRevents & POLLIN) revents |= FDEVENT_IN;
    if (pollRevents & POLLOUT) revents |= FDEVENT_OUT;
    if (pollRevents & POLLERR) revents |= FDEVENT_ERR;
    if (pollRevents & POLLHUP) revents |= FDEVENT_HUP;
    if (pollRevents & POLLNVAL) revents |= FDEVENT_NVAL;
    if (pollRevents & POLLPRI) revents |= FDEVENT_PRI;

    return revents;
}

int CFdEventPoll::getEventFileDescriptor(size_t node)
{
    return this->pollfds[node].fd;
}

int CFdEventPoll::getEventNextFileDescriptor(int node)
{
    size_t i  = (node < 0) ? 0 : node + 1;

    for (; i < this->used; i++)
    {
	if (this->pollfds[i].revents)
            return i;
    }

    return -1;
}

}

#endif
